import os
import re
import threading

from PyQt4 import QtCore

from ninja_ide.core import settings

#Lock to protect the file's writing operation
file_store_content_lock = threading.Lock()


class NinjaIOException(Exception):
    """
    IO operation's exception
    """
    pass


def create_init_file(folderName):
    if not os.path.isdir(folderName):
        raise NinjaIOException("The destination folder does not exist")
    name = os.path.join(folderName, '__init__.py')
    f = open(name, 'w')
    f.flush()
    f.close()


def create_init_file_complete(folderName):
    if not os.path.isdir(folderName):
        raise NinjaIOException("The destination folder does not exist")
    patDef = re.compile('^def .+')
    patClass = re.compile('^class .+')
    patExt = re.compile('.+\\.py')
    files = os.listdir(folderName)
    files = filter(patExt.match, files)
    files.sort()
    imports_ = []
    for f in files:
        read = open(os.path.join(folderName, f), 'r')
        imp = [re.split('\\s|\\(', line)[1] for line in read.readlines()
                if patDef.match(line) or patClass.match(line)]
        imports_ += ['from ' + f[:-3] + ' import ' + i for i in imp]
    name = os.path.join(folderName, '__init__.py')
    fi = open(name, 'w')
    for import_ in imports_:
        fi.write(import_ + '\n')
    fi.flush()
    fi.close()


def create_folder(folderName):
    if os.path.exists(folderName):
        raise NinjaIOException("The folder already exist")
    os.mkdir(folderName)
    create_init_file(folderName)


def create_tree_folders(folderName):
    if os.path.exists(folderName):
        raise NinjaIOException("The folder already exist")
    os.makedirs(folderName)


def folder_exists(folderName):
    return os.path.isdir(folderName)


def file_exists(path, fileName=''):
    if fileName != '':
        path = os.path.join(path, fileName)
    return os.path.isfile(path)


def _search_coding_line(txt):
    #search a pattern like this: # -*- coding: utf-8 -*-
    coding_pattern = "coding[:=]\s*([-\w.]+)"
    return re.search(coding_pattern, txt)


def read_file_content(fileName):
    if not os.path.exists(fileName):
        raise NinjaIOException("The file does not exist")
    if not os.path.isfile(fileName):
        raise NinjaIOException("%s is not a file" % fileName)
    f = QtCore.QFile(fileName)
    if not f.open(QtCore.QIODevice.ReadOnly | QtCore.QIODevice.Text):
        raise NinjaIOException("%s" % f.errorString())
    #QIODevice.Text convert \r\n to \n
    stream = QtCore.QTextStream(f)
    content = stream.readAll()
    coding_line = _search_coding_line(content)
    if coding_line:
        encoding = coding_line.groups()[0].toUpper()
        #reset the stream options
        stream.reset()
        stream.seek(0)
        #Set the specific encoding
        stream.setCodec(encoding)
        content = stream.readAll()
    f.close()
    return unicode(content)


def get_basename(fileName):
    return os.path.basename(fileName)


def get_folder(fileName):
    return os.path.dirname(fileName)


def _real_store_file_content(fileName, content):
    global file_store_content_lock
    file_store_content_lock.acquire()
    try:
        f = QtCore.QFile(fileName)
        if not f.open(QtCore.QIODevice.WriteOnly | QtCore.QIODevice.Truncate):
            raise NinjaIOException(f.errorString())
        #QTextStream detect locales ;)
        stream = QtCore.QTextStream(f)
        coding_line = _search_coding_line(content)
        if coding_line:
            encoding = coding_line.groups()[0].toUpper()
            stream.setCodec(encoding)
        encoded_stream = stream.codec().fromUnicode(content)
        f.write(encoded_stream)
        f.flush()
        f.close()
    except:
        raise
    finally:
        file_store_content_lock.release()


def store_file_content(fileName, content):
    if fileName == '':
        raise Exception()
    ext = (os.path.splitext(fileName)[-1])[1:]
    if ext == '':
        fileName += '.py'
    t = threading.Thread(target=_real_store_file_content,
                            args=(fileName, content))
    t.start()
    #wait until the saver finish
    t.join()
    return fileName


def open_project(path):
    if not os.path.exists(path):
        raise NinjaIOException("The folder already exist")
    d = {}
    for root, dirs, files in os.walk(path):
        d[root] = [[f for f in files
                if (os.path.splitext(f.lower())[-1]) in \
                settings.SUPPORTED_EXTENSIONS],
                dirs]
    return d


def open_project_with_extensions(path, extensions):
    if not os.path.exists(path):
        raise NinjaIOException("The folder already exist")
    d = {}
    for root, dirs, files in os.walk(path):
        d[root] = [[f for f in files
                if (os.path.splitext(f.lower())[-1]) in extensions],
                dirs]
    return d


def delete_file(path, fileName=None):
    if fileName:
        path = os.path.join(path, fileName)
    if os.path.isfile(path):
        os.remove(path)


def rename_file(old, new):
    if os.path.isfile(old):
        ext = (os.path.splitext(new)[-1])[1:]
        if ext == '':
            new += '.py'
        os.rename(old, new)
        return new
    return ''


def get_file_extension(fileName):
    return (os.path.splitext(fileName.lower())[-1])


def get_module_name(fileName):
    module = os.path.basename(fileName)
    return (os.path.splitext(module)[0])


def convert_to_relative(basePath, fileName):
    if fileName.startswith(basePath):
        fileName = fileName.replace(basePath, '')
        if fileName.startswith(os.path.sep):
            fileName = fileName[1:]
    return fileName


def create_abs_path(basePath, fileName):
    return os.path.join(basePath, fileName)


def belongs_to_folder(path, fileName):
    return fileName.startswith(path)


def get_last_modification(fileName):
    return QtCore.QFileInfo(fileName).lastModified()


def has_write_permission(fileName):
    return os.access(fileName, os.W_OK)


def check_for_external_modification(fileName, old_mtime):
    new_modification_time = get_last_modification(fileName)
    #check the file mtime attribute calling os.stat()
    if new_modification_time > old_mtime:
        return True
    return  False


def get_files_from_folder(folder, ext):
    filesExt = os.listdir(folder)
    filesExt = [f for f in filesExt if f.endswith(ext)]
    return filesExt
